/*
 * Server.cpp
 *
 *  Created on: 27 f�vr. 2013
 *      Author: Aurel
 */

#include "Network/Server.hpp"

Server::Server() : 		Observer(), 		m_Listener(), 		m_Selector(),
m_PlayerList(),			m_GameEngine(0),	m_MutexCoupleSocket(0)
{
	this->m_MutexCoupleSocket 	= new sf::Mutex();
	this->m_isRunning 			= true;

	if (!m_Listener.Listen(DefaultValue::NETWORK_PORT))
		Debug::printTrace(Debug::information,"Network/Server.Constructor", "Port Disable to Listen");
	else
	{
		this->m_GameEngine = new GameEngine(true);
		this->m_GameEngine->Launch();

		this->m_GameEngine->AddObserver(this);

		Debug::printTrace(Debug::information,"Network/Server.Constructor", "Listening Port");
		m_Selector.Add(m_Listener);

		this->Launch();
	}
}

Server::~Server() {
	this->Stop();
	this->m_GameEngine->DeletteObserver(this);
	this->m_GameEngine->Stop();
	this->RemoveAllCoupleSocketPlayer();
	delete this->m_GameEngine;
	delete this->m_MutexCoupleSocket;
}

/**
 * Stop the thread du server
 */
void Server::Stop()
{
	this->m_isRunning = false;
	this->Wait();
}
/**
 * Receive messages from clients
 */
void Server::Run()
{
	while(m_isRunning)
	{
		// Get the sockets ready for reading
		unsigned int NbSockets = m_Selector.Wait(NetworkValues::NETWORK_TIMEOUT);

		// We can read from each returned socket
		for (unsigned int i = 0; i < NbSockets; ++i)
		{
			// Get the current socket
			sf::SocketTCP Socket = m_Selector.GetSocketReady(i);

			if (Socket == m_Listener)
			{
				// If the listening socket is ready, it means that we can accept a new connection
				sf::IPAddress Address;
				sf::SocketTCP Client;
				m_Listener.Accept(Client, &Address);

				Debug::printTrace(Debug::information,"display/ServerScreen::LaunchServeur", "New client Connected");

				// Add it to the selector
				m_Selector.Add(Client);
			}
			else
			{
				// Else, it is a client socket so we can read the data he sent
				sf::Packet l_Packet;

				if (Socket.Receive(l_Packet) == sf::Socket::Done)
				{
					sf::Packet l_PacketMessage = l_Packet;
					int code;

					l_Packet >> code;

					//Debug::printTrace(Debug::information,"display/ServerScreen::LaunchServeur => Client code ", Text::float2string(code));
					switch (code) {
					case NetworkValues::NETWORK_CODE_CONNECTION:
						this->AnswerConnection(Socket, &l_PacketMessage);
						break;
					case NetworkValues::NETWORK_CODE_DIRECTION_PLAYER:
						this->AnswerNewDirection(Socket, &l_PacketMessage);
						break;
					case NetworkValues::NETWORK_CODE_SHOOT:
						this->AnswerNewShoot(Socket, &l_PacketMessage);
						break;
					case NetworkValues::NETWORK_CODE_DECONNEXION:
						this->AnswerDeconnexion(Socket, &l_PacketMessage);
						break;
						default:
							break;
					}

					//Debug::printTrace(Debug::information,"display/ServerScreen::LaunchServeur => Client say", Message);
				}
				else
				{
					// Error : we'd better remove the socket from the selector
					m_Selector.Remove(Socket);
				}
			}
		}
	}
}
/**
 * Create an Answer for the new client
 */
void Server::AnswerConnection(sf::SocketTCP p_socket, sf::Packet* p_packet)
{
	int ms_code;
	string ms_Name;

	(*p_packet) >> ms_code >> ms_Name;

	Debug::printTrace(Debug::information,"NetworkServer::AnswerConnection => Conexion asked by player : ", ms_Name);

	Character* l_character = this->m_GameEngine->AddPlayer(0,ms_Name,Character::WAITING,0, 0, Character::None, Character::None,0,0);

	this->AddCoupleSocketPlayer(p_socket, l_character);

	// The gameEngine Noty the other player
	// we accept the new player and send him the old players
	sf::Packet 		l_packetAnswer;
	int 			l_code 				= NetworkValues::NETWORK_CODE_ANSWER_CONNECTION;

	l_packetAnswer << l_code << l_character->GetID();

	p_socket.Send(l_packetAnswer);

	//Now Send the old players to the new one
	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		CouplePlayerSocket* l_couple = m_PlayerList[i];

		if(l_couple->m_player != l_character)
		{
			sf::Packet l_packetNewOld;
			l_packetNewOld 	<< NetworkValues::NETWORK_CODE_NEW_PLAYER
							<< l_couple->m_player->GetName()
							<< l_couple->m_player->GetID()
							<< l_couple->m_player->GetLife()
							<< l_couple->m_player->Getposition().x
							<< l_couple->m_player->Getposition().y
							<< l_couple->m_player->GetDirectionVertical()
							<< l_couple->m_player->GetDirectionHorizontal()
							<< l_couple->m_player->GetState()
							<< l_couple->m_player->GetKill()
							<< l_couple->m_player->GetDeath();

			p_socket.Send(l_packetNewOld);

			Debug::printTrace(Debug::information,"NetworkServer::AnswerConnection", "send old player");

		}
	}
	this->m_MutexCoupleSocket->Unlock();
}
/**
 * Create an Answer for the new client
 */
void Server::AnswerNewDirection(sf::SocketTCP p_socket, sf::Packet* p_packet)
{
	int ms_code;
	Character::Direction ms_directionV, ms_directionH;

	//l_packet << NetworkValues::NETWORK_CODE_DIRECTION_PLAYER << p_DirectionH << p_DirectionV;
	(*p_packet) >> ms_code >> ms_directionH >> ms_directionV;

	Character* l_character = this->GetPlayerFromSocket(p_socket);

	l_character->SetDirectionHorizontal(ms_directionH);
	l_character->SetDirectionVertical(ms_directionV);

	/*Debug::printTrace(Debug::information,"NetworkServer::AnswerNewDirection => id player", Text::float2string(l_character->GetID()));
	Debug::printTrace(Debug::information,"NetworkServer::AnswerNewDirection => code", Text::float2string(ms_code));
	Debug::printTrace(Debug::information,"NetworkServer::AnswerNewDirection => Vertical", Text::float2string(ms_directionV));
	Debug::printTrace(Debug::information,"NetworkServer::AnswerNewDirection => Horyzontal", Text::float2string(ms_directionH));*/

	// We Notify all ! Let's go
	sf::Packet 		l_packetAnswer;

	l_packetAnswer 	<< NetworkValues::NETWORK_CODE_DIRECTION_PLAYER
					<< l_character->GetID()
					<< l_character->Getposition().x
					<< l_character->Getposition().y
					<< l_character->GetDirectionHorizontal()
					<< l_character->GetDirectionVertical();

	this->SendPacket(&l_packetAnswer, 0);
}
/**
 * Create a shoot if possible
 */
void Server::AnswerNewShoot(sf::SocketTCP p_socket, sf::Packet* p_packet)
{
	int ms_code;
	float ms_directionV, ms_directionH;

	//l_packet << NetworkValues::NETWORK_CODE_DIRECTION_PLAYER << p_DirectionH << p_DirectionV;
	(*p_packet) >> ms_code >> ms_directionH >> ms_directionV;

	Character* l_character = this->GetPlayerFromSocket(p_socket);

	this->m_GameEngine->Shoot(l_character, sf::Vector2f(ms_directionH, ms_directionV));
}
/**
 * Delete a player
 */
void Server::AnswerDeconnexion(sf::SocketTCP p_socket, sf::Packet* p_packet)
{
	Character* l_character = this->GetPlayerFromSocket(p_socket);

	Debug::printTrace(Debug::information,"NetworkServer::AnswerDeconnexion", "notify players");
	//notify before to get the player id
	this->NotifyPlayerLeave(l_character);

	Debug::printTrace(Debug::information,"NetworkServer::AnswerDeconnexion", "before remove player");
	this->m_GameEngine->RemovePlayer(l_character);
	Debug::printTrace(Debug::information,"NetworkServer::AnswerDeconnexion", "after  remove player");
}
/**
 * Caught the event from the gameEngine (New players, shots etc...)
 */
void Server::Notify(int code, void* parameter)
{
    switch(code)
    {
    case GameEngine::NOTIFICATION_CODE_NEW_PLAYER :
        this->NotifyNewPlayer((Character*)parameter);
    break;
    case GameEngine::NOTIFICATION_CODE_SPAWN_PLAYER :
        this->NotifyPlayerSpawn((Character*)parameter);
    break;
    case GameEngine::NOTIFICATION_CODE_DELETE_PLAYER :
        //nothing
    break;
    case GameEngine::NOTIFICATION_CODE_NEW_SHOT :
        this->NotifyNewShoot((Shot*)parameter);
    break;
    case GameEngine::NOTIFICATION_CODE_DELETE_SHOT :
        //nothing
    break;
    case GameEngine::NOTIFICATION_CODE_PLAYER_HIT :
    	this->NotifyPlayerHit((Character*)parameter);
    break;
    case GameEngine::NOTIFICATION_CODE_PLAYER_KILL :
    	this->NotifyPlayerKill((Character*)parameter);
    break;
    case GameEngine::NOTIFICATION_CODE_PLAYER_DIE :
    	this->NotifyPlayerDie((Character*)parameter);
    break;
    }
}
/**
 * Notify everybody that a new player has joined the game
 */
void Server::NotifyNewPlayer(Character* p_character){

	// we notify the others
	sf::Packet l_packetNotify;

	Debug::printTrace(Debug::information,"NetworkServer::NotifyNewPlayer", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_NEW_PLAYER
					<< p_character->GetName()
					<< p_character->GetID()
					<< p_character->GetLife()
					<< p_character->Getposition().x
					<< p_character->Getposition().y
					<< p_character->GetDirectionVertical()
					<< p_character->GetDirectionHorizontal()
					<< p_character->GetState()
					<< p_character->GetKill()
					<< p_character->GetDeath();

	this->SendPacket(&l_packetNotify, p_character);
}
/**
 * Notify everybody that a player has respawn
 */
void Server::NotifyPlayerSpawn(Character* p_character){

	// we notify the others
	sf::Packet l_packetNotify;

	//Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerSpawn", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_SPAWN_PLAYER
					<< p_character->GetID()
					<< p_character->Getposition().x
					<< p_character->Getposition().y;

	this->SendPacket(&l_packetNotify, 0);
}
/**
 * Notify everybody that a player shooted
 */
void Server::NotifyNewShoot(Shot* p_newShoot){

	// we notify the others
	sf::Packet l_packetNotify;

	//Debug::printTrace(Debug::information,"NetworkServer::NotifyNewShoot", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_SHOOT
					<< p_newShoot->GetFrom()->GetID()
					<< p_newShoot->GetAngle();

	this->SendPacket(&l_packetNotify, 0);
}
/**
 * Notify everybody that a player has been hit
 */
void Server::NotifyPlayerHit(Character* p_character){

	// we notify the others
	sf::Packet l_packetNotify;

	Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerHIT", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_PLAYER_HIT
					<< p_character->GetID()
					<< p_character->GetLife()
					<< p_character->GetState();

	this->SendPacket(&l_packetNotify, 0);
}
/**
 * Notify everybody that a player kill somebody
 */
void Server::NotifyPlayerKill(Character* p_character){

	// we notify the others
	sf::Packet l_packetNotify;

	Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerKill", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_PLAYER_KILL
					<< p_character->GetID()
					<< p_character->GetKill();

	this->SendPacket(&l_packetNotify, 0);
}
/**
 * Notify everybody that a player is dead
 */
void Server::NotifyPlayerDie(Character* p_character){

	// we notify the others
	sf::Packet l_packetNotify;

	Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerDie", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_PLAYER_DIE
					<< p_character->GetID()
					<< p_character->GetDeath();

	this->SendPacket(&l_packetNotify, 0);
}
/**
 * Notify everybody that a player leave the game
 * send the id player to the client
 */
void Server::NotifyPlayerLeave(Character* p_character)
{
	//remove the couple
	this->RemoveCoupleSocketPlayer(this->GetSocketFromPlayer(p_character));
	// we notify the others
	sf::Packet l_packetNotify;

	Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerLeave", "");

	l_packetNotify 	<< NetworkValues::NETWORK_CODE_DECONNEXION
					<< p_character->GetID();

	this->SendPacket(&l_packetNotify, 0);

	Debug::printTrace(Debug::information,"NetworkServer::NotifyPlayerLeave", "packet sent");
}
/**
 * Add a new couple in the liste
 */
void Server::AddCoupleSocketPlayer(sf::SocketTCP p_socket, Character* p_player)
{
	CouplePlayerSocket* l_couple = new CouplePlayerSocket();
	l_couple->m_Socket = p_socket;
	l_couple->m_player = p_player;

	this->m_MutexCoupleSocket->Lock();
	this->m_PlayerList.insert(this->m_PlayerList.end(), l_couple);
	this->m_MutexCoupleSocket->Unlock();
}
/**
 * Remove a couple and delete the associated player
 */
void Server::RemoveCoupleSocketPlayer(sf::SocketTCP p_socket)
{
	CouplePlayerSocket* l_couple;

	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		l_couple = m_PlayerList[i];

		if(p_socket == l_couple->m_Socket)
		{
			m_PlayerList.erase(m_PlayerList.begin() + i);
			//delete l_couple->m_player; BUG Crash server
			delete l_couple;
			break;
		}
	}
	this->m_MutexCoupleSocket->Unlock();
}
/**
 * Remove all the couple
 */
void Server::RemoveAllCoupleSocketPlayer()
{
	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		CouplePlayerSocket* l_couple = m_PlayerList[i];

		delete l_couple->m_player;
		delete l_couple;

	}
	this->m_PlayerList.clear();
	this->m_MutexCoupleSocket->Unlock();
}
/**
 * Get the player associated to the socket in parameter
 * 0 if doesn't exist
 */
Character* Server::GetPlayerFromSocket(sf::SocketTCP p_socket)
{
	Character* l_player = 0;

	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		if(p_socket == m_PlayerList[i]->m_Socket)
		{
			l_player = m_PlayerList[i]->m_player;
			break;
		}
	}
	this->m_MutexCoupleSocket->Unlock();
	return l_player;
}
/**
 * Get the socket associated to the player in parameter
 * if doesn't exist, return the listener
 */
sf::SocketTCP Server::GetSocketFromPlayer(Character* p_Player)
{
	CouplePlayerSocket* l_couple;
	sf::SocketTCP l_socket = this->m_Listener;

	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		l_couple = m_PlayerList[i];

		if(p_Player == l_couple->m_player)
		{
			l_socket = l_couple->m_Socket;
		}
	}
	this->m_MutexCoupleSocket->Unlock();

	return l_socket;
}
/**
 * Send a packet to each player. The player parameter will not receive the packet.
 */
void Server::SendPacket(sf::Packet* p_packetNotify, Character* p_character)
{
	Debug::printTrace(Debug::information,"NetworkServer::SendPacket", "");
	this->m_MutexCoupleSocket->Lock();
	for (unsigned int i(0) ; i < this->m_PlayerList.size() ; i++)
	{
		CouplePlayerSocket* l_couple = m_PlayerList[i];

		if(l_couple->m_player != p_character)
		{
			sf::Packet l_transfertPacket = (* p_packetNotify);
			sf::Socket::Status l_socketStatus = l_couple->m_Socket.Send(l_transfertPacket);
			if(l_socketStatus == sf::Socket::Done)
			{
				Debug::printTrace(Debug::critical,"Network/Server::SendPacket", "Packet send");
			}
			else
			{
				Debug::printTrace(Debug::critical,"Network/Server::SendPacket", "SOCKET DEAD");
			}
		}
	}
	this->m_MutexCoupleSocket->Unlock();
}
